package org.concepts.java.generics;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;

/**
 * Show how we can use generics in our data structures to make the type contract
 * more clear and also catch errors during compile time.
 * <p/>
 * After going through the examples, take quiz at <a target="_blank"
 * href="http://java.sun.com/docs/books/
 * tutorial/java/generics/QandE/generics-questions.html">http://java.sun.com/docs/books/tutorial/java/generics/QandE/generics-questions.html<
 * / a >
 * 
 * 
 * <h1>Reference:</h1>
 * 
 * <ul>
 * <li><a target="_blank"
 * href="http://java.sun.com/docs/books/tutorial/java/generics/index.html"
 * >http://java.sun.com/docs/books/tutorial/java/generics/index.html</a></li>
 * 
 * @see
 * @author vvvv
 * 
 */
public class TestCarsInDataStructures {

  /**
   * Show that user did not clearly express the contract while creating an
   * ArrayList of cars, which resulted in CCE later.
   */
  @Test(expected = ClassCastException.class)
  public void testNonGenericsCollection() {
    Collection cars = new ArrayList();
    cars.add(new Honda());
    cars.add(new Ford());

    for (Object car : cars) {
      System.out.println(car);
    }

    for (Object car : cars) {
      System.out.println((Ford) car);
    }

  }

  /**
   * Show that by specifying type of a collection we don't need to cast while
   * retrieving values. Also CCE are caught during compile time.
   * <p/>
   * Also see cage examplae at <a href=
   * "http://java.sun.com/docs/books/tutorial/java/generics/subtyping.html"
   * >http://java.sun.com/docs/books/tutorial/java/generics/subtyping.html</a>.
   */
  @Test
  public void testGenericsCollection() {
    List<Ford> fords = new ArrayList<Ford>();

    fords.add(new Ford());
    fords.add(new Tauras());

    Ford ford = fords.get(0);
    Assert.assertNotNull(ford);
    System.out.println(ford);

    Tauras tauras = (Tauras) fords.get(1);
    Assert.assertNotNull(tauras);
    System.out.println(tauras);

    // Uncomment the line below and you'll get compilation errors
    // fords.add(new Honda());
  }

  /**
   * Show that we need to use "? extends Type" syntax in method params to allow
   * it to access list of subtypes.
   */
  @Test
  public void testGenericsWithSubTypeCollection() {
    List<Ford> fords = new ArrayList<Ford>();

    fords.add(new Ford());

    printFords(fords);

    List<Tauras> taurases = new ArrayList<Tauras>();
    taurases.add(new Tauras());

    // works fine
    printFordsAndSubTypes(taurases);

    // uncomment line below and you'll get compilation error
    // printFords(taurases);
  }

  private void printFords(List<Ford> fords) {
    for (Ford ford : fords) {
      System.out.println("ford [" + ford + "]");
    }
  }

  private void printFordsAndSubTypes(List<? extends Ford> fords) {
    for (Ford ford : fords) {
      System.out.println("ford [" + ford + "]");
    }
  }

}
